package operation;

import DAO.CitationDAO;
import entity.text.ChapterEntity;
import entity.text.PaperEntity;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ：hzs
 * @date ：Created in 2020/12/10 15:25
 * @description：
 * @modified By：
 * @version: $
 */
abstract public class AbstractFileOperation {

    /**
     * 源文件
     */
    private File file;
    /**
     * 论文文本
      */
    protected String paperText = null;
    /**
     * 0：初始；1：正常；2：没有reference；3：参考文献没序号
      */
    private int index = 0;

    protected List<String[]> spliter;

    private PaperEntity paperEntity = new PaperEntity();

    protected CitationDAO citationDAO = new CitationDAO();

    abstract public String loadTextFromFile(File file);

    abstract public PaperEntity getPaperEntityFromFile(File file) throws Exception;

    private static Pattern CONTENTS_PATTERN = Pattern.compile("\nContents");

    private static Pattern ACKNOWLEDGEMENT_PATTERN = Pattern.compile("\nAcknowledgement");

    private static Pattern APPENDIX_PATTERN = Pattern.compile("\nAppendix");

    private static Pattern NEWLINE_PATTERN = Pattern.compile("\r\n");

    private static Pattern SPACE_NEWLINE_PATTERN = Pattern.compile("\\s*\r\n");

    private static Pattern REFERENCE_PATTERN = Pattern.compile("\nReference");

    private static Pattern DOC_PATTERN = Pattern.compile("\\. \\. \\.");

    protected double countWordOneLine(File file) {
        double result = - 1.0;
        try {
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String s = bufferedReader.readLine();
            int i = 0;
            int sum = 0;
            ArrayList<Integer> lengthList = new ArrayList<>();
            while (s != null) {
                sum += s.length();
                i++;
                lengthList.add(s.length());
                s = bufferedReader.readLine();
            }
            double aver = lengthList.stream().mapToInt(a->a).summaryStatistics().getAverage();
            result = lengthList.stream().filter(a -> a > aver).mapToInt(a -> a).summaryStatistics().getAverage();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    public String removeReference(String inputText) {
        String referencePattern = "\nReferences\n";
        Matcher m = Pattern.compile(referencePattern).matcher(inputText);
        if (m.find()) {
            inputText = inputText.substring(0, m.start());
        } else {
            System.out.println("找不到参考文献");
        }
        return inputText;
    }

    public int readLineAlong(String inputText, int startIndex) {
        int end = startIndex;
        if (inputText.charAt(end + 1) == '\n') {
            end++;
        }
        do {
            end++;
        } while (inputText.charAt(end) != '\n');
        return end + 1;
    }

    public int[] readLineCurrent(CharSequence inputText, int index) {
        int start = index;
        do {
            start--;
        } while (inputText.charAt(start) != '\n');
        int end = index;
        do {
            end++;
        } while (inputText.charAt(end) != '\n');
        int[] result = new int[2];
        result[0] = start + 1;
        result[1] = end + 1;
        return result;
    }

    public List<String> getReferenceFromLongText(String input) {
        //前面已经去掉页眉页脚
        List<String> result = new ArrayList<>();
        Matcher m = null;
        //哪一种类型的序号?
        int labelType = -1;
        int referenceType = -1;
        int label = 2;
        if (input.startsWith("1 ")) {
            //数字加空格
            labelType = 1;
        } else if (input.startsWith("1.")) {
            labelType = 2;
        } else if (input.startsWith("1")) {
            labelType = 3;
        } else if (input.startsWith("[1]")) {
            labelType = 4;
        } else {
            index = 3;
            return result;
        }

        //没有序号怎么办？没有序号就不用做了

        String leftPattern = null;
        String rightPattern = null;
        switch (labelType) {
            case 1:
                leftPattern = "\r\n *";
                rightPattern = " *";
                break;
            case 2:
                leftPattern = "\r\n *";
                rightPattern = "\\. *";
                break;
            case 3:
                leftPattern = "\r\n *";
                rightPattern = "";
                break;
            case 4:
                leftPattern = "\r\n *\\[";
                rightPattern = "\\] *";
                break;
            default:
                ;
        }

        m = Pattern.compile(leftPattern + label + rightPattern).matcher(input);
        m.find();

        do {
            int referenceEnd = m.start();
            String reference = input.substring(0, referenceEnd);

            result.add(reference);

            label++;
            input = input.substring(referenceEnd + 2);
            m = Pattern.compile(leftPattern + label + rightPattern).matcher(input);
        } while (m.find());

        //补上最后一条reference，最后一条到哪算完？连续两个/r/n算完
        boolean foundLastReference = false;
        String lastReference = "";
        do {
            m = NEWLINE_PATTERN.matcher(input);
            m.find();
            lastReference = lastReference.concat(input.substring(0, m.start()));
            input = input.substring(m.end());
            m = SPACE_NEWLINE_PATTERN.matcher(input);
            if (m.lookingAt()) {
                foundLastReference = true;
            }
            if ("".equals(input.trim())) {
                foundLastReference = true;
            }
        } while (!foundLastReference);
        result.add(lastReference);

        return result;
    }

    /**
     * create by: mmzs
     * description: 倒序读一行
     * create time: 2020/7/21 16:27
     *
     * @return
     * @Param: null
     */
    public int readLineReverse(CharSequence input, int endIndex) {
        int start = endIndex;
        if (input.charAt(start - 1) == '\n') {
            start--;
        }
        do {
            start--;
        } while (input.charAt(start) != '\n');
        return start + 1;
    }

    public void extractPaperAbstract() {
        String patterns = "\nAbstract";
        if (this.paperText.contains("Contents lists available at")) {
            String[] keywords = this.paperEntity.getKeywords().split(";");
            patterns = "\n" + keywords[keywords.length - 1];
        }
        Matcher m = Pattern.compile(patterns).matcher(this.paperText);
        if (m.find()) {
            int newLineStart = m.end();
            int newLineEnd = readLineAlong(paperText, newLineStart);
            StringBuilder stringBuilder = new StringBuilder();
            String newLineText = paperText.substring(newLineStart, newLineEnd);
            while (!newLineText.startsWith("1. ")) {
                stringBuilder.append(newLineText);
                newLineStart = newLineEnd;
                newLineEnd = readLineAlong(paperText, newLineStart);
                newLineText = paperText.substring(newLineStart, newLineEnd);
            }
            this.paperEntity.setPaperAbstract(stringBuilder.toString());
        }
    }

    protected String getFileNameCode(File file) {
        String fileName = file.getName();
        if (fileName.contains("-")) {
            return fileName.split("-")[2];
        }
        return fileName.split("\\.")[0];
    }

    /**
     * 去掉content和acknowledgement
     */
    protected void preSplit() {
        try {
            paperText = paperText.replace("\n?", "\n");
            removeContents();
            removeAcknowledgementsAndSoOn();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void doSplit() {
//        扫描文本的指针
        int leftPointer = 0;
        int rightPointer = 0;
//        抽取结果放这里
        String[] container = new String[3];
        int firstNumber = 0;
        Matcher m1 = null;
//        准备好了，开始抽
        do {
//            如果不是第一次找标题
            if (firstNumber > 1) {
                rightPointer = m1.start();
                container[2] = paperText.substring(leftPointer, rightPointer);
                spliter.add(container);
                container = new String[3];
            }
//            如果不是第一次循环
            if (firstNumber != 0) {
//                假设现在firstNumber是2，可这里先处理1的标题
//            序号
                container[0] = String.valueOf(firstNumber);
//            小标题
                leftPointer = m1.end();
                rightPointer = readLineAlong(paperText, leftPointer);
                container[1] = paperText.substring(leftPointer, rightPointer);
//            正文，可能为空，开始第二层
                leftPointer = rightPointer;
                Matcher m2 = null;
                int secondNumber = 0;
                do {
//                    如果不是第一次找
                    if (secondNumber != 0) {
                        rightPointer = m2.start();
                        container[2] = paperText.substring(leftPointer, rightPointer);
                        spliter.add(container);
                        container = new String[3];
//                      序号
                        container[0] = String.valueOf(firstNumber + "." + secondNumber);
//                      小标题
                        leftPointer = m2.end();
                        rightPointer = readLineAlong(paperText, leftPointer);
                        container[1] = paperText.substring(leftPointer, rightPointer);
//                      正文，可能为空，开始第三层
                        leftPointer = rightPointer;
                        Matcher m3 = null;
                        int thirdNumber = 0;
                        do {
//                            如果不是第一次找
                            if (thirdNumber != 0) {
                                rightPointer = m3.start();
                                try {
                                    container[2] = paperText.substring(leftPointer, rightPointer);
                                } catch (Exception e) {
//                                    有时会出现content为空的情况，例如一个二级标题紧贴三级标题
                                    container[2] = null;
                                }
                                spliter.add(container);
                                container = new String[3];
//                      序号
                                container[0] = String.valueOf(firstNumber + "." + secondNumber + "." + thirdNumber);
//                      小标题
                                leftPointer = m3.end();
                                rightPointer = readLineAlong(paperText, leftPointer);
                                container[1] = paperText.substring(leftPointer, rightPointer);
//                      正文，可能为空，开始第四层
                                leftPointer = rightPointer;
                                Matcher m4 = null;
                                int fourthNumber = 0;
                                do {
                                    if (fourthNumber != 0) {
                                        rightPointer = m4.start();
                                        container[2] = paperText.substring(leftPointer, rightPointer);
                                        spliter.add(container);
                                        container = new String[3];
//                      序号
                                        container[0] = String.valueOf(firstNumber + "." + secondNumber
                                                + "." + thirdNumber + "." + fourthNumber);
//                      小标题
                                        leftPointer = m4.end();
                                        rightPointer = readLineAlong(paperText, leftPointer);
                                        container[1] = paperText.substring(leftPointer, rightPointer);
                                    }
                                    leftPointer = rightPointer;
                                    fourthNumber++;
                                    String pattern4 = "\n" + firstNumber + "\\." + secondNumber + "."
                                            + thirdNumber + "." + fourthNumber + "\\. ";
                                    m4 = Pattern.compile(pattern4).matcher(paperText);
                                } while (m4.find(leftPointer));
                            }
                            thirdNumber++;
                            String pattern3 = "\n" + firstNumber + "\\."
                                    + secondNumber + "\\." + thirdNumber + "\\. ";
                            m3 = Pattern.compile(pattern3).matcher(paperText);
                        } while (m3.find(leftPointer));
                    }
                    secondNumber++;
                    String pattern2 = "\n" + firstNumber + "\\." + secondNumber + "\\. ";
                    m2 = Pattern.compile(pattern2).matcher(paperText);
                } while (m2.find(leftPointer));

            }
            firstNumber++;
            String pattern1 = "\n" + firstNumber + "\\. ";
            m1 = Pattern.compile(pattern1).matcher(paperText);
        } while (m1.find(leftPointer));
//        收尾
        container[2] = paperText.substring(leftPointer);
        spliter.add(container);
    }

    protected void afterSplit(PaperEntity paperEntity) {
        List<ChapterEntity> chapterEntities = paperEntity.getChapterEntities();
        if (chapterEntities == null) {
            chapterEntities = new ArrayList<>();
        }
        for (String[] strings : this.spliter) {
            ChapterEntity chapterEntity = new ChapterEntity();
            chapterEntity.setIndex(strings[0]);
            chapterEntity.setSubTitle(strings[1]);
            chapterEntity.setContent(strings[2]);
            chapterEntities.add(chapterEntity);
        }
        paperEntity.setChapterEntities(chapterEntities);
    }

    private  void removeContents() {
        Matcher matcher = CONTENTS_PATTERN.matcher(paperText);
        if (matcher.find()) {
            int i = matcher.end();
            int length = paperText.length();
            while (i < length) {
                char c = paperText.charAt(i);
                if (c == '\r' || c == '\n') {
                    i++;
                } else {
                    break;
                }
            }
            Matcher matcher2 = DOC_PATTERN.matcher(paperText);
            if (matcher2.find()) {
                int j = matcher2.start();
                String firstChapterTitle = paperText.substring(i, j);
                Matcher matcher3 = Pattern.compile(firstChapterTitle.trim()).matcher(paperText);
                if (matcher3.find(j)) {
                    paperText = paperText.substring(0, matcher.start()).concat("\n").concat(paperText.substring(matcher3.start()));
                }
            }
        }
    }

    private void removeAcknowledgementsAndSoOn() {
        Matcher matcher = ACKNOWLEDGEMENT_PATTERN.matcher(paperText);
        int length = paperText.length();
        // 从文章的后半部分开始查
        if (matcher.find(length/2)) {
            this.paperText = paperText.substring(0, matcher.start());
        }
        matcher = APPENDIX_PATTERN.matcher(paperText);
        if (matcher.find(length/2)) {
            this.paperText = paperText.substring(0, matcher.start());
        }
        matcher = REFERENCE_PATTERN.matcher(paperText);
        if (matcher.find(length/2)) {
            this.paperText = paperText.substring(0, matcher.start());
        }
    }

    public String removeHeaderAndFooter(String input) throws Exception {
        String s = removeHeaderAndFooterOnNewVersionPdf(input);
        if ("fail".equals(s)) {
            s = removeHeaderAndFooterOnOldVersionPdf(input);
        }
        return s;
    }

    public String removeHeaderAndFooterOnNewVersionPdf(String input) {
        String headerPattern = "/ Artificial Intelligence \\d+ \\(\\d+\\) \\d+\\–\\d+";
        Matcher m = Pattern.compile(headerPattern).matcher(input);
        if (m.find()) {
            int start = m.start();
            int end = m.end();
            String twoChar = null;
            do {
                start--;
                twoChar = input.substring(start - 1, start);
            } while (!"\n".equals(twoChar));
            String header = input.substring(start, end);
            String[] headerSplit = header.split(" ");
            String[] pages = headerSplit[headerSplit.length - 1].split("–");
            int startPage = Integer.parseInt(pages[0]);
            int endPage = Integer.parseInt(pages[1]);
            // 有可能末尾把下一行的首位数字加进去了
            if (endPage > 10*startPage && endPage > 99) {
                int possibleEndPage = Integer.parseInt(pages[1].substring(0, pages[1].length()-1));
                if (possibleEndPage > startPage) {
                    endPage = possibleEndPage;
                    header = input.substring(start, end-1);
                }
            }

//            这里是第一次出现页眉的位置,开始往上找页脚
            int footerEnd = start;
            int footerLineStart = start;
            int footerLineEnd = -1;
            String footerLine = null;
            do {
                footerLineEnd = footerLineStart;
                footerLineStart = readLineReverse(input, footerLineEnd);
                footerLine = input.substring(footerLineStart, footerLineEnd);
            } while (isFooterLine(footerLine));
//            页脚找到了，去掉页脚
            String footerText = input.substring(footerLineEnd, footerEnd);
            input = input.replace(footerText, "\n");

//            开始找页眉
            // 形如这种“188 P. Baroni et al. / Artificial Intelligence 165 (2005) 187–259”
            if (startPage % 2 == 1 && header.startsWith(Integer.toString(startPage + 1))) {
                // 改成“P. Baroni et al. / Artificial Intelligence 165 (2005) 187–259”
                header = header.substring(pages[0].length() + 1);
            }
//            构造页眉，奇数在右，偶数在左
            for (int i = startPage + 1; i <= endPage; i++) {
                String textToBeReplaced = null;
                if (i % 2 == 1) {
                    textToBeReplaced = header + " " + i;
                } else {
                    textToBeReplaced = "" + i + " " + header;
                }
                if (input.contains(textToBeReplaced)) {
//                    有些没有页眉
                    input = input.replace(textToBeReplaced, "\n");
//                    页眉被去掉了
                }
            }
        } else {
            return "fail";
        }
        return input;
    }

    public String removeHeaderAndFooterOnOldVersionPdf(String input) {
        String headerPattern = "/Ar\\S+ I";
        StringBuilder result = new StringBuilder(input);
        Matcher m = Pattern.compile(headerPattern).matcher(result);
        int i = 0;
        while (m.find()) {
            int[] ints = readLineCurrent(result, m.start());
            if (i == 0) {
                i = 1;
//                这里是第一次出现页眉的位置,开始往上找页脚
                int footerEnd = ints[0];
                int footerLineStart = ints[0];
                int footerLineEnd = -1;
                String footerLine = null;
                do {
                    footerLineEnd = footerLineStart;
                    footerLineStart = readLineReverse(result, footerLineEnd);
                    footerLine = result.substring(footerLineStart, footerLineEnd);
                } while (isFooterLine(footerLine));
//            页脚找到了，去掉页脚
                result.replace(footerLineEnd, footerEnd, "\n");
            } else {
                result.replace(ints[0], ints[1], "\n");
            }
            m.reset();
        }
        return result.toString();
    }

    public boolean isFooterLine(String input) {
        String[] arr = new String[]{
                "http://","https://","All rights reserved","Corresponding author",
                "doi:","URL:","Tel.","PII","pii","Email:","SSDI","E-mail","phone"
        };
        for (String s : arr) {
            if (input.contains(s)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 抽取引文
     * @param paperEntity
     * @param citationIndex
     * @return
     */
    public List<String> extractorCitationSentence(PaperEntity paperEntity, String citationIndex) {
        citationIndex = citationIndex.replaceAll("\\[|\\]", "");
        List<String> citations = new ArrayList<>();
        List<ChapterEntity> chapterEntities = paperEntity.getChapterEntities();
        String pattern = "\\[(\\d+,)?" + citationIndex + "(,\\d+)?\\]";
        chapterEntities.forEach(a -> {
            String content = a.getContent();
            Matcher m = Pattern.compile(pattern).matcher(content);
            while (m.find()) {
                int start = m.start();
                if (start != 0) {
                    do {
                        start--;
                    } while (!isSentenceBeginning(content, start));
                }
                int end = m.end();
                if (end != content.length()) {
                    do {
                        end++;
                    } while (!isSentenceEnding(content, end));
                }
                citations.add(content.substring(start, end));
            }
        });
        return citations;
    }

    public List<String> extractorCitationParagraph(PaperEntity paperEntity, String citationIndex) {
        citationIndex = citationIndex.replaceAll("\\[|\\]", "");
        double lineLength = paperEntity.getLineLength();
        List<String> citations = new ArrayList<>();
        List<ChapterEntity> chapterEntities = paperEntity.getChapterEntities();
        String pattern = "\\[(\\d+,)?" + citationIndex + "(,\\d+)?\\]";
        chapterEntities.forEach(a -> {
            String content = a.getContent();
            Matcher m = Pattern.compile(pattern).matcher(content);
            while (m.find()) {
                int start = m.start();
                int end = m.end();
                String citation = null;
//                当前行
                int[] currentLineIndex = readLineCurrent(content, start);
                String currentLineText = content.substring(currentLineIndex[0], currentLineIndex[1]);
                start = currentLineIndex[0];
                end = currentLineIndex[1];

                int lastLineStart = readLineReverse(content, start);
                String lastLineText = content.substring(lastLineStart, start);

                int nextLineEnd = readLineAlong(content, end);
                String nextLineText = content.substring(end, nextLineEnd);
//                你这个nowline又是啥？一个逐行跳的指针
                String nowLine = currentLineText;
//                两个条件：1、当前行小写字母开头；2、上一行不是句号问号感叹号结尾；
//                两条件满足一个就继续循环
//                再加个条件，字数要够
//                再加个条件，是否到达段落边缘
                while (!"".equals(lastLineText) &&
                        ( Character.isLowerCase(nowLine.charAt(0))
                                || !(lastLineText.endsWith(".\n") || lastLineText.endsWith(". \n") || lastLineText.endsWith("?\n") || lastLineText.endsWith("!\n"))
                                || lastLineText.length() > 0.75*lineLength)
                ){
                    start = lastLineStart;
                    nowLine = lastLineText;
                    lastLineStart = readLineReverse(content, start);
                    lastLineText = content.substring(lastLineStart, start);
                }
//                向下搜，同样的方法，再来一次
                nowLine = currentLineText;
                while (!"".equals(nextLineText) &&
                        (Character.isLowerCase(nextLineText.charAt(0))
                                || !(nowLine.endsWith(".\n") || nowLine.endsWith(". \n") || nowLine.endsWith("?\n") || nextLineText.endsWith("!\n"))
                                || nowLine.length() > 0.75*lineLength)
                ){
                    end = nextLineEnd;
                    nowLine = nextLineText;
                    nextLineEnd = readLineAlong(content, end);
                    nextLineText = content.substring(end, nextLineEnd);
                }

                citation = content.substring(start, end);
                citations.add(citation);
            }
        });
        return citations;
    }

    private boolean isSentenceBeginning(String s, int position) {
        if (position == 0) {
            return true;
        }
        if ((s.charAt(position-1) == ' ' || s.charAt(position-1) == '\n')
                && Character.isUpperCase(s.charAt(position))) {
            if (position-2 >= 0
                    &&(s.charAt(position-2) == '.' || s.charAt(position-2) == '？')) {
                return true;
            }
            return false;
        }
        return false;
    }

    private boolean isSentenceEnding(String s, int position) {
        if (position == 0) {
            return false;
        }
        if (position == s.length()) {
            return true;
        }
        if ((s.charAt(position) == ' ' || s.charAt(position) == '\n')
                && (s.charAt(position-1) == '.' || s.charAt(position-1) == '?')) {
            return true;
        }
        return false;
    }
}
